{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [Dictionaries](https://docs.python.org/3/library/stdtypes.html#dict) \n",
    "Collections of `key`-`value` pairs. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_empty_dict = {}  # alternative: my_empty_dict = dict()\n",
    "print(f\"dict: {my_empty_dict}, type: {type(my_empty_dict)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Initialization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict1 = {\"value1\": 1.6, \"value2\": 10, \"name\": \"John Doe\"}\n",
    "dict2 = dict(value1=1.6, value2=10, name=\"John Doe\")\n",
    "\n",
    "print(dict1)\n",
    "print(dict2)\n",
    "\n",
    "print(f\"equals: {dict1 == dict2}\")\n",
    "print(f\"length: {len(dict1)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `dict.keys(), dict.values(), dict.items()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(f\"keys: {dict1.keys()}\")\n",
    "print(f\"values: {dict1.values()}\")\n",
    "print(f\"items: {dict1.items()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Accessing and setting values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {}\n",
    "my_dict[\"key1\"] = \"value1\"\n",
    "my_dict[\"key2\"] = 99\n",
    "my_dict[\"key1\"] = \"new value\"  # overriding existing value\n",
    "print(my_dict)\n",
    "print(f\"value of key1: {my_dict['key1']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Accessing a nonexistent key will raise `KeyError` (see [`dict.get()`](#dict_get) for workaround):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# print(my_dict['nope'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Deleting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {\"key1\": \"value1\", \"key2\": 99, \"keyX\": \"valueX\"}\n",
    "del my_dict[\"keyX\"]\n",
    "print(my_dict)\n",
    "\n",
    "# Usually better to make sure that the key exists (see also pop() and popitem())\n",
    "key_to_delete = \"my_key\"\n",
    "if key_to_delete in my_dict:\n",
    "    del my_dict[key_to_delete]\n",
    "else:\n",
    "    print(f\"{key_to_delete} is not in {my_dict}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dictionaries are mutable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {\"ham\": \"good\", \"carrot\": \"semi good\"}\n",
    "my_other_dict = my_dict\n",
    "my_other_dict[\"carrot\"] = \"super tasty\"\n",
    "my_other_dict[\"sausage\"] = \"best ever\"\n",
    "print(f\"{my_dict=}\\nother: {my_other_dict}\")\n",
    "print(f\"equals: {my_dict == my_other_dict}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a new `dict` if you want to have a copy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {\"ham\": \"good\", \"carrot\": \"semi good\"}\n",
    "my_other_dict = dict(my_dict)\n",
    "my_other_dict[\"beer\"] = \"decent\"\n",
    "print(f\"{my_dict=}\\nother: {my_other_dict}\")\n",
    "print(f\"equals: {my_dict == my_other_dict}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='dict_get'></a>\n",
    "## `dict.get()`\n",
    "Returns `None` if `key` is not in `dict`. However, you can also specify `default` return value which will be returned if `key` is not present in the `dict`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n",
    "value_of_d = my_dict.get(\"d\")\n",
    "print(f\"d: {value_of_d}\")\n",
    "\n",
    "value_of_d = my_dict.get(\"d\", \"my default value\")\n",
    "print(f\"d: {value_of_d}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `dict.pop()`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = dict(food=\"ham\", drink=\"beer\", sport=\"football\")\n",
    "print(f\"dict before pops: {my_dict}\")\n",
    "\n",
    "food = my_dict.pop(\"food\")\n",
    "print(f\"food: {food}\")\n",
    "print(f\"dict after popping food: {my_dict}\")\n",
    "\n",
    "food_again = my_dict.pop(\"food\", \"default value for food\")\n",
    "print(f\"food again: {food_again}\")\n",
    "print(f\"dict after popping food again: {my_dict}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `dict.setdefault()`\n",
    "Returns the `value` of `key` defined as first parameter. If the `key` is not present in the dict, adds `key` with default value (second parameter)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_dict = {\"a\": 1, \"b\": 2, \"c\": 3}\n",
    "a = my_dict.setdefault(\"a\", \"my default value\")\n",
    "d = my_dict.setdefault(\"d\", \"my default value\")\n",
    "print(f\"a: {a}\\nd: {d}\\nmy_dict: {my_dict}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `dict.update()`\n",
    "Merge two `dict`s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "dict1 = {\"a\": 1, \"b\": 2}\n",
    "dict2 = {\"c\": 3}\n",
    "dict1.update(dict2)\n",
    "print(dict1)\n",
    "\n",
    "# If they have same keys:\n",
    "dict1.update({\"c\": 4})\n",
    "print(dict1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The keys of a `dict` have to be immutable"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Thus you can not use e.g. a `list` or a `dict` as key because they are mutable types\n",
    ":"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# bad_dict = {['my_list'], 'value'}  # Raises TypeError"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Values can be mutable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "good_dict = {\"my key\": [\"Python\", \"is\", \"still\", \"cool\"]}\n",
    "print(good_dict)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
